package io.noties.markwon.base.html

import io.noties.markwon.base.MarkwonVisitor

class MarkwonHtmlRendererImpl internal constructor(
    private val allowNonClosedTags: Boolean,
    private val tagHandlers: Map<String, TagHandler>
) : MarkwonHtmlRenderer {
    override fun render(
        visitor: MarkwonVisitor,
        parser: MarkwonHtmlParser
    ) {
        val end: Int = if (!allowNonClosedTags) {
            HtmlTag.NO_END
        } else {
            visitor.length()
        }
        val theme=visitor.configuration().categoryTheme()

        parser.flushInlineTags(end) { tags ->
            for (inline in tags) {
                // if tag is not closed -> do not render
                if (!inline.isClosed) {
                    continue
                }
               tagHandler(inline.name())?.handle(visitor, this@MarkwonHtmlRendererImpl, inline)
            }
        }
        parser.flushBlockTags(end, object : MarkwonHtmlParser.FlushAction<HtmlTag> {
            override fun apply(tags: List<HtmlTag>) {
                var handler: TagHandler?
                for (block in tags) {
                    if (!block.isClosed) {
                        continue
                    }
                    handler = tagHandler(block.name())
                    if (handler != null) {
                        theme.nodeNext(block.name())
                        handler.handle(visitor, this@MarkwonHtmlRendererImpl, block)
                    } else {
                        // see if any of children can be handled
                        if(block is HtmlTag.Block) {
                            theme.nodeIn(block.name())
                            apply(block.children())
                            theme.nodeOut(block.name())
                        }
                    }
                }
            }
        })
        parser.reset()
    }

    override fun tagHandler(tagName: String): TagHandler? {
        return tagHandlers[tagName]
    }

    class Builder {
        private val tagHandlers = mutableMapOf<String, TagHandler>()
        private var allowNonClosedTags = false
        private var excludeDefaults = false
        private var isBuilt = false
        fun allowNonClosedTags(allowNonClosedTags: Boolean) {
            checkState()
            this.allowNonClosedTags = allowNonClosedTags
        }

        fun addHandler(tagHandler: TagHandler) {
            checkState()
            for (tag in tagHandler.supportedTags()) {
                tagHandlers[tag] = tagHandler
            }
        }

        fun getHandler(tagName: String): TagHandler? {
            checkState()
            return tagHandlers[tagName]
        }

        fun excludeDefaults(excludeDefaults: Boolean) {
            checkState()
            this.excludeDefaults = excludeDefaults
        }

        fun excludeDefaults(): Boolean {
            return excludeDefaults
        }

        fun build(): MarkwonHtmlRenderer {
            checkState()
            isBuilt = true

            // okay, let's validate that we have at least one tagHandler registered
            // if we have none -> return no-op implementation
            return if (tagHandlers.isNotEmpty())
                MarkwonHtmlRendererImpl(allowNonClosedTags, tagHandlers)
            else
                MarkwonHtmlRendererNoOp()
        }

        private fun checkState() {
            check(!isBuilt) { "Builder has been already built" }
        }

        fun addDefaultTagHandler(tagHandler: TagHandler) {
            for (tag in tagHandler.supportedTags()) {
                if (!tagHandlers.containsKey(tag)) {
                    tagHandlers[tag] = tagHandler
                }
            }
        }
    }
}